﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace Kokomo
{
	[DebuggerDisplay("Address = {Address}, Size = {Size}")]
	public class UnmanagedBuffer : CriticalFinalizerObject, IDisposable
	{
		public static UnmanagedBuffer Allocate(int size)
		{
			IntPtr address = Marshal.AllocCoTaskMem(size);
			return new UnmanagedBuffer(address, size);
		}

		protected UnmanagedBuffer(IntPtr address, int size)
		{
			this.Address = address;
			this.Size = size;
		}
		~UnmanagedBuffer()
		{
			this.Dispose();
		}

		public IntPtr Address { get; private set; }
		public int Size { get; private set; }

		#region IDisposable Members

		public void Dispose()
		{
			if (this.Address != IntPtr.Zero)
			{
				this.Free();
				this.Address = IntPtr.Zero;
			}
		}

		private void Free()
		{
			Marshal.FreeCoTaskMem(this.Address);
		}

		#endregion

		public void Write(byte[] formatData)
		{
			this.Write(formatData, 0, formatData.Length);
		}
		public void Write(byte[] formatData, int offset, int count)
		{
			ArgumentValidation.CheckNullArgument("formatData", formatData);
			Marshal.Copy(formatData, offset, this.Address, count);
		}
		public byte[] ReadAllBytes()
		{
			return this.ReadBytes(0, this.Size);
		}
		public byte[] ReadBytes(int offset, int count)
		{
			byte[] buffer = new byte[count];
			Marshal.Copy(new IntPtr(this.Address.ToInt32() + offset), buffer, 0, buffer.Length);

			return buffer;
		}
		public string ReadString(CharSet charset)
		{
			Func<IntPtr, string> method;
			switch (charset)
			{
				case CharSet.Ansi:
					method = Marshal.PtrToStringAnsi;
					break;
				case CharSet.Auto:
					method = Marshal.PtrToStringAuto;
					break;
				case CharSet.Unicode:
					method = Marshal.PtrToStringUni;
					break;
				default:
			throw new ArgumentException("Invalid CharSet value", "charset");
			}

			string text = method(this.Address);

			return text;
		}


		public void WriteStruct<TStruct>(TStruct value)
		{
			Marshal.StructureToPtr(value, this.Address, false);
		}
		public TStruct ReadStruct<TStruct>()
		{
			return this.Address.ToMarshaledStruct<TStruct>();
		}
	}
}
